package jmine.tec.di.scope;

import java.util.HashMap;
import java.util.Map;

/**
 * @author takeshi.fukushima
 */
public abstract class AbstractTypedScope implements TypedScope {

    private volatile TypedScope parent;

    /**
     * @param parent the parent to set
     */
    public final void setParent(TypedScope parent) {
        this.parent = parent;
    }

    /**
     * {@inheritDoc}
     */
    public final boolean containsKey(String key) {
        return this.localContainsKey(key) || (this.parent != null && this.parent.containsKey(key));
    }

    /**
     * @param key the key
     * @return boolean
     */
    protected abstract boolean localContainsKey(String key);

    /**
     * @param key the key
     * @param type the type
     * @return Object
     */
    protected abstract Object localGet(String key, Class<?> type);

    /**
     * {@inheritDoc}
     */
    public final <T> T get(String key, Class<T> clazz) {
        Object local = this.localGet(key, clazz);
        if (local == null && this.parent != null) {
            return this.parent.get(key, clazz);
        }
        return clazz.cast(local);
    }

    /**
     * @return the parent
     */
    public final TypedScope getParent() {
        return this.parent;
    }

    /**
     * {@inheritDoc}
     */
    public final void putDefinition(String key, Object value) {
        Object localValue = value;
        if ((localValue instanceof String) && ("".equals(localValue))) {
            localValue = null;
        }
        if (this.parent != null) {
            this.parent.putDefinition(key, localValue);
        } else {
            this.put(key, value);
        }
    }

    /**
     * {@inheritDoc}
     */
    public final Map<String, Object> getProperties() {
        Map<String, Object> map = new HashMap<String, Object>();
        if (this.parent != null) {
            map.putAll(this.parent.getProperties());
        }
        map.putAll(this.getLocalProperties());
        return map;
    }

    /**
     * Subclasses devem implementar este metodo e devolver somente as propriedades locais
     * 
     * @return {@link Map}
     */
    protected abstract Map<String, Object> getLocalProperties();

}
